home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
101-125
/
113
/
dosdev
/
device.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
33KB
|
1,225 lines
/*
* DOSDEVICE.C V1.10 2 November 1987
*
* EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN.
*
* By Matthew Dillon.
*
* Debugging routines are disabled by simply attempting to open the
* file "debugoff", turned on again with "debugon". No prefix may be
* attached to these names (you must be CD'd to TEST:).
*
* See Documentation for a detailed discussion.
*
* BUGS:
* Currently the only known bug is with the implementation of the
* RAM disk itself. Specifically, if filehandle A is at the end of
* the file, and somebody appends to the file with another filehandle,
* B, filehandle A will get confused as to it's current position in
* the file.
*
* I am probably not updating all the right timestamps. This is
* easy to fix... All you have to do is fool with the floppy and
* see which datestamps get updated for certain operations.
*/
#include "dos.h"
/*
* Since this code might be called several times in a row without being
* unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!! This also goes
* for any global/static assignments that might be changed by running the
* code.
*/
PROC *DosProc; /* Our Process */
DEVNODE *DosNode; /* Our DOS node.. created by DOS for us */
DEVLIST *DevList; /* Device List structure for our volume node */
void *SysBase; /* EXEC library base */
DOSLIB *DOSBase; /* DOS library base for debug process */
RAMFILE RFRoot; /* Directory/File structure (root node) */
LIST FHBase; /* Open Files */
LIST LCBase; /* Open Locks */
long TotalBytes; /* total bytes of data in filesystem */
/* DEBUGGING */
PORT *Dbport; /* owned by the debug process */
PORT *Dback; /* owned by the DOS device driver */
short DBDisable;
MSG DummyMsg; /* Dummy message that debug proc can use */
/*
* Don't call the entry point main(). This way, if you make a mistake
* with the compile options you'll get a link error.
*/
void
noname()
{
register PACKET *packet;
register short error;
MSG *msg;
ubyte notdone;
ubyte buf[256];
void *tmp;
/*
* Initialize all global variables. SysBase MUST be initialized before
* we can make Exec calls. AbsExecBase is a library symbol
* referencing absolute memory location 4. The DOS library is openned
* for the debug process only.
*/
DBDisable = 0; /* Init. globals */
Dbport = Dback = NULL;
TotalBytes = 0;
SysBase = AbsExecBase;
DOSBase = OpenLibrary("dos.library",0);
DosProc = FindTask(NULL);
{
WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */
msg = GetMsg(&DosProc->pr_MsgPort);
packet = (PACKET *)msg->mn_Node.ln_Name;
/*
* Loading DosNode->dn_Task causes DOS *NOT* to startup a new
* instance of the device driver for every reference. E.G. if
* you were writing a CON device you would want this field to
* be NULL.
*/
if (DOSBase) {
DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
register DEVLIST *dl = dosalloc(sizeof(DEVLIST));
DosNode = BTOC(packet->dp_Arg3);
/*
* Create Volume node and add to the device list. This will
* cause the WORKBENCH to recognize us as a disk. If we don't
* create a Volume node, Wb will not recognize us. However,
* we are a RAM: disk, Volume node or not.
*/
DevList = dl;
dl->dl_Type = DLT_VOLUME;
dl->dl_Task = &DosProc->pr_MsgPort;
dl->dl_DiskType = ID_DOS_DISK;
dl->dl_Name = (void *)DosNode->dn_Name;
dl->dl_Next = di->di_DevInfo;
di->di_DevInfo = (long)CTOB(dl);
/*
* Set dn_Task field which tells DOS not to startup a new
* process on every reference.
*/
DosNode->dn_Task = &DosProc->pr_MsgPort;
packet->dp_Res1 = DOS_TRUE;
packet->dp_Res2 = 0;
} else { /* couldn't open dos.library */
packet->dp_Res1 = DOS_FALSE;
returnpacket(packet);
return; /* exit process */
}
returnpacket(packet);
}
/*
* Initialize debugging code
*/
dbinit(); /* this can be removed */
/* Initialize RAM disk */
{
ubyte *ptr = BTOC(DosNode->dn_Name);
short len = *ptr;
NewList(&FHBase); /* more globals */
NewList(&LCBase);
bzero(&RFRoot,sizeof(RFRoot));
RFRoot.type = FILE_DIR; /* root directory */
DateStamp(&RFRoot.date); /* datestamp */
NewList(&RFRoot.list); /* sub dirs */
RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /* Root NAME */
bmov(ptr+1,RFRoot.name,len);
RFRoot.name[len] = 0;
dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
}
/*
* Here begins the endless loop, waiting for requests over our
* message port and executing them. Since requests are sent over
* our message port, this precludes being able to call DOS functions
* ourselves (that is why the debugging routines are a separate process)
*/
top:
for (notdone = 1; notdone;) {
WaitPort(&DosProc->pr_MsgPort);
while (msg = GetMsg(&DosProc->pr_MsgPort)) {
register ubyte *ptr;
packet = (PACKET *)msg->mn_Node.ln_Name;
packet->dp_Res1 = DOS_TRUE;
packet->dp_Res2 = 0;
error = 0;
dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
packet->dp_Type,
packet->dp_Arg1, packet->dp_Arg2,
packet->dp_Arg3,
typetostr(packet->dp_Type)
);
switch(packet->dp_Type) {
case ACTION_DIE: /* attempt to die? */
notdone = 0; /* try to die */
break;
case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */
case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */
case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
char *ptr;
btos(packet->dp_Arg3,buf);
dbprintf("'%s' ", buf);
if (strcmp(buf,"debugoff") == 0)
DBDisable = 1;
if (strcmp(buf,"debugon") == 0)
DBDisable = 0;
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
if (ramfile->type == FILE_DIR) {
error = ERROR_OBJECT_WRONG_TYPE;
goto openbreak;
}
if (ramfile->locks < 0) {
error = ERROR_OBJECT_IN_USE;
goto openbreak;
}
if (packet->dp_Type == ACTION_OPENOLD) {
++ramfile->locks;
} else {
if (ramfile->locks > 0) {
error = ERROR_OBJECT_IN_USE;
} else {
if (packet->dp_Type == ACTION_OPENNEW) {
freedata(ramfile);
ramfile->protection = 0;
}
--ramfile->locks;
}
}
} else {
if (!parentdir) {
error = ERROR_INVALID_COMPONENT_NAME;
goto openbreak;
}
if (packet->dp_Type == ACTION_OPENNEW) {
ramfile = createramfile(parentdir, FILE_FILE, ptr);
--ramfile->locks;
} else {
error = ERROR_OBJECT_NOT_FOUND;
}
}
if (!error) {
register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR);
((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)mfh;
mfh->file = ramfile;
mfh->fentry = GetHead(&ramfile->list);
AddHead(&FHBase,mfh);
}
}
openbreak:
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen = mfh->fentry;
register ubyte *ptr = (ubyte *)packet->dp_Arg2;
register long left = packet->dp_Arg3;
register long scr;
while (left && fen) {
scr = fen->bytes - mfh->offset;
if (left < scr) {
bmov(fen->buf + mfh->offset, ptr, left);
mfh->offset += left;
left = 0;
} else {
bmov(fen->buf + mfh->offset, ptr, scr);
left -= scr;
ptr += scr;
mfh->base += fen->bytes;
mfh->offset = 0;
fen = NextNode(fen);
}
}
mfh->fentry = fen;
packet->dp_Res1 = packet->dp_Arg3 - left;
}
break;
case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen = (FENTRY *)mfh->fentry;
ubyte *ptr = (ubyte *)packet->dp_Arg2;
long left = packet->dp_Arg3;
long scr;
/*
* Doesn't work right if multiple readers/appenders.
*/
while (left) {
if (fen) {
dbprintf("FEN: %ld left: %ld\n", fen->bytes, left);
scr = fen->bytes - mfh->offset;
if (left < scr) {
if (fen->bytes < mfh->offset + left)
dbprintf("PANIC! AWR0\n");
else
bmov(ptr, fen->buf + mfh->offset, left);
mfh->offset += left;
left = 0;
} else {
if (fen->bytes < mfh->offset + scr)
dbprintf("PANIC! AWR1\n");
else
bmov(ptr, fen->buf + mfh->offset, scr);
ptr += scr;
left -= scr;
mfh->base += fen->bytes;
mfh->offset = 0;
fen = NextNode(fen);
}
} else {
fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
if (fen->buf = AllocMem(left, MEMF_PUBLIC)) {
fen->bytes = left;
mfh->file->bytes += left;
mfh->base += left;
mfh->offset = 0;
TotalBytes += left;
AddTail(&mfh->file->list, fen);
dbprintf("NEWFEN: (%ld)\n", fen->bytes);
bmov(ptr, fen->buf, left);
left = 0;
} else {
FreeMem(fen, sizeof(FENTRY));
dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left);
mfh->offset = 0;
break;
}
fen = NULL; /* cause append */
}
}
packet->dp_Res1 = packet->dp_Arg3 - left;
mfh->fentry = fen;
}
break;
case ACTION_CLOSE: /* FHArg1 Bool:TRUE */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register RAMFILE *file = mfh->file;
Remove(mfh);
FreeMem(mfh,sizeof(*mfh));
if (--file->locks < 0)
file->locks = 0;
}
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition*/
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen;
register long absseek;
packet->dp_Res1 = mfh->base + mfh->offset;
absseek = packet->dp_Arg2;
if (packet->dp_Arg3 == 0)
absseek += mfh->base + mfh->offset;
if (packet->dp_Arg3 == 1)
absseek = mfh->file->bytes + absseek;
if (absseek < 0 || absseek > mfh->file->bytes) {
error = ERROR_SEEK_ERROR;
break;
}
mfh->base = mfh->offset = 0;
/*
* Stupid way to do it but....
*/
for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) {
if (mfh->base + fen->bytes > absseek) {
mfh->offset = absseek - mfh->base;
break;
}
mfh->base += fen->bytes;
}
mfh->fentry = fen;
}
break;
/*
* This implementation sucks. The right way to do it is with
* a hash table. The directory must be searched for the file
* name, then the next entry retrieved. If the next entry is
* NULL there are no more entries. If the filename could not
* be found we return the first entry, if any.
*
* You can't simply keep a pointer around to the next node
* because it can be moved or removed at any time.
*/
case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */
{
register FIB *fib = BTOC(packet->dp_Arg2);
register RAMFILE *dir = getlockfile(packet->dp_Arg1);
register RAMFILE *file;
if (dir->type == FILE_FILE) {
error = ERROR_OBJECT_WRONG_TYPE;
break;
}
file = GetHead(&dir->list);
if (fib->fib_DiskKey) {
register int len = *(ubyte *)fib->fib_FileName;
for (; file; file = NextNode(file)) {
if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len))
break;
}
if (file)
file = NextNode(file);
else
file = GetHead(&dir->list);
}
fib->fib_DiskKey = 1;
error = -1;
if (!(tmp=file)) {
error = ERROR_NO_MORE_ENTRIES;
break;
}
}
/* fall through */
case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */
{
register FIB *fib;
register RAMFILE *file;
register RAMFILE *dummy;
fib = BTOC(packet->dp_Arg2);
if (error) {
file = tmp; /* fall through from above */
} else {
file = getlockfile(packet->dp_Arg1);
fib->fib_DiskKey = 0;
}
error = 0;
fib->fib_DirEntryType = file->type;
strcpy(fib->fib_FileName+1, file->name);
fib->fib_FileName[0] = strlen(file->name);
fib->fib_Protection = file->protection;
fib->fib_EntryType = NULL;
fib->fib_Size = file->bytes;
fib->fib_NumBlocks = file->bytes >> 9;
fib->fib_Date = file->date;
if (file->comment) {
strcpy(fib->fib_Comment+1, file->comment);
fib->fib_Comment[0] = strlen(file->comment);
} else {
fib->fib_Comment[0] = 0;
}
}
break;
case ACTION_INFO: /* Lock, InfoData Bool:TRUE */
tmp = BTOC(packet->dp_Arg2);
error = -1;
/* fall through */
case ACTION_DISK_INFO: /* InfoData Bool:TRUE */
{
register INFODATA *id;
/*
* Note: id_NumBlocks is never 0, but only to get
* around a bug I found in my shell (where I divide
* by id_NumBlocks). Other programs probably break
* as well.
*/
(error) ? (id = tmp) : (id = BTOC(packet->dp_Arg1));
error = 0;
bzero(id, sizeof(*id));
id->id_DiskState = ID_VALIDATED;
id->id_NumBlocks = (TotalBytes >> 9) + 1;
id->id_NumBlocksUsed = (TotalBytes >> 9) + 1;
id->id_BytesPerBlock = 512;
id->id_DiskType = ID_DOS_DISK;
id->id_VolumeNode = (long)CTOB(DosNode);
id->id_InUse = (long)GetHead(&LCBase);
}
break;
case ACTION_PARENT: /* Lock ParentLock */
{
register RAMFILE *file = getlockfile(packet->dp_Arg1);
if (file->type == FILE_FILE) {
error = ERROR_OBJECT_NOT_FOUND;
break;
}
if (file->locks < 0) {
error = ERROR_OBJECT_IN_USE;
break;
}
if (file->parent)
packet->dp_Res1 = (long)CTOB(ramlock(file->parent, ACCESS_READ));
else
error = ERROR_OBJECT_NOT_FOUND;
}
break;
case ACTION_DELETE_OBJECT: /*Lock,Name Bool */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
btos(packet->dp_Arg2, buf);
if (ramfile = searchpath(&parentdir,buf,NULL)) {
if (ramfile->locks || ramfile == &RFRoot) {
error = ERROR_OBJECT_IN_USE;
break;
}
if (ramfile->type == FILE_DIR) {
if (GetHead(&ramfile->list))
error = ERROR_DIRECTORY_NOT_EMPTY;
} else {
freedata(ramfile);
}
if (!error) {
freeramfile(ramfile);
DateStamp(&parentdir->date);
}
} else {
if (!parentdir)
error = ERROR_INVALID_COMPONENT_NAME;
else
error = ERROR_OBJECT_NOT_FOUND;
}
}
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_CREATE_DIR: /* Lock,Name Lock */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
char *ptr;
btos(packet->dp_Arg2, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
error = ERROR_OBJECT_EXISTS;
break;
}
if (!parentdir) {
error = ERROR_INVALID_COMPONENT_NAME;
break;
}
ramfile = createramfile(parentdir, FILE_DIR, ptr);
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE));
}
break;
case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
btos(packet->dp_Arg2, buf);
dbprintf("'%s' %ld ", buf, packet->dp_Arg3);
if (ramfile = searchpath(&parentdir,buf,NULL)) {
if (ramfile->locks < 0 || (ramfile->locks && packet->dp_Arg3 == ACCESS_WRITE)) {
error = ERROR_OBJECT_IN_USE;
break;
}
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, packet->dp_Arg3));
} else {
if (!parentdir)
error = ERROR_INVALID_COMPONENT_NAME;
else
error = ERROR_OBJECT_NOT_FOUND;
}
}
break;
case ACTION_COPY_DIR: /* Lock, Lock */
{
register RAMFILE *ramfile = getlockfile(packet->dp_Arg1);
if (ramfile->locks < 0)
error = ERROR_OBJECT_IN_USE;
else
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_READ));
}
break;
case ACTION_FREE_LOCK: /* Lock, Bool */
if (packet->dp_Arg1);
ramunlock(BTOC(packet->dp_Arg1));
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_SET_PROTECT:/* -,Lock,Name,Mask Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
char *ptr;
btos(packet->dp_Arg3, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
ramfile->protection = packet->dp_Arg4;
} else {
if (parentdir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
case ACTION_SET_COMMENT:/* -,Lock,Name,Comment Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
char *ptr;
btos(packet->dp_Arg3, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
btos(packet->dp_Arg4, buf);
if (ramfile->comment)
FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC);
strcpy(ramfile->comment, buf);
} else {
if (parentdir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName Bool */
{
register RAMFILE *file1;
RAMFILE *sourcedir = getlockfile(packet->dp_Arg1);
RAMFILE *destdir = getlockfile(packet->dp_Arg3);
char *ptr;
btos(packet->dp_Arg2,buf);
dbprintf("\nRENAME '%s' (%ld) ", buf, strlen(buf));
if (file1 = searchpath(&sourcedir,buf,NULL)) {
btos(packet->dp_Arg4,buf);
dbprintf("TO '%s' (%ld)", buf, strlen(buf));
if (searchpath(&destdir,buf,&ptr)) {
error = ERROR_OBJECT_EXISTS;
} else {
if (destdir) {
if (file1 == destdir) { /* moving inside self */
error = ERROR_OBJECT_IN_USE;
break;
}
dbprintf("REN '%s' %ld", ptr, strlen(ptr));
DateStamp(&sourcedir->date);
DateStamp(&destdir->date);
/*FreeMem(file1->name, strlen(file1->name)+1);*/
Remove(file1);
file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC);
file1->parent = destdir;
strcpy(file1->name, ptr);
AddHead(&destdir->list, file1);
} else {
error = ERROR_INVALID_COMPONENT_NAME;
}
}
} else {
if (sourcedir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
/*
* A few other packet types which we do not support
*/
case ACTION_INHIBIT: /* Bool Bool */
/* Return success for the hell of it */
break;
case ACTION_RENAME_DISK:/* BSTR:NewName Bool */
case ACTION_MORECACHE: /* #BufsToAdd Bool */
case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */
case ACTION_FLUSH: /* writeout bufs, disk motor off */
case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */
default:
error = ERROR_ACTION_NOT_KNOWN;
break;
}
if (packet) {
if (error) {
dbprintf("ERR=%ld\n", error);
packet->dp_Res1 = DOS_FALSE;
packet->dp_Res2 = error;
} else {
dbprintf("RES=%06lx\n", packet->dp_Res1);
}
returnpacket(packet);
}
}
}
dbprintf("Can we remove ourselves? ");
Delay(50); /* I wanna even see the debug message! */
Forbid();
if (packetsqueued(DosProc) || GetHead(&FHBase) || GetHead(&LCBase)
|| GetHead(&RFRoot.list)) {
Permit();
dbprintf(" .. not yet!\n");
goto top; /* sorry... can't exit */
}
/*
* Causes a new process to be created on next reference
*/
DosNode->dn_Task = FALSE;
/*
* Remove Volume entry. Since DOS uses singly linked lists, we
* must (ugg) search it manually to find the link before our
* Volume entry.
*/
{
DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
register DEVLIST *dl;
register void *dlp;
dlp = &di->di_DevInfo;
for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next))
dlp = &dl->dl_Next;
if (dl == DevList) {
*(BPTR *)dlp = dl->dl_Next;
dosfree(dl);
} else {
dbprintf("****PANIC: Unable to find volume node\n");
}
}
/*
* Remove debug process, closedown, fall of the end of the world
* (which is how you kill yourself if a PROCESS. A TASK would have
* had to RemTask(NULL) itself).
*/
dbuninit();
CloseLibrary(DOSBase);
}
/*
* PACKET ROUTINES. Dos Packets are in a rather strange format as you
* can see by this and how the PACKET structure is extracted in the
* GetMsg() of the main routine.
*/
void
returnpacket(packet)
register struct DosPacket *packet;
{
register struct Message *mess;
register struct MsgPort *replyport;
replyport = packet->dp_Port;
mess = packet->dp_Link;
packet->dp_Port = &DosProc->pr_MsgPort;
mess->mn_Node.ln_Name = (char *)packet;
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(replyport, mess);
}
/*
* Are there any packets queued to our device?
*/
packetsqueued()
{
return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
(void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
}
/*
* DOS MEMORY ROUTINES
*
* DOS makes certain assumptions about LOCKS. A lock must minimally be
* a FileLock structure, with additional private information after the
* FileLock structure. The longword before the beginning of the structure
* must contain the length of structure + 4.
*
* NOTE!!!!! The workbench does not follow the rules and assumes it can
* copy lock structures. This means that if you want to be workbench
* compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
*/
void *
dosalloc(bytes)
register ulong bytes;
{
register ulong *ptr;
bytes += 4;
ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
*ptr = bytes;
return(ptr+1);
}
dosfree(ptr)
register ulong *ptr;
{
--ptr;
FreeMem(ptr, *ptr);
}
/*
* Convert a BSTR into a normal string.. copying the string into buf.
* I use normal strings for internal storage, and convert back and forth
* when required.
*/
void
btos(bstr,buf)
ubyte *bstr;
ubyte *buf;
{
bstr = BTOC(bstr);
bmov(bstr+1,buf,*bstr);
buf[*bstr] = 0;
}
/*
* Some EXEC list handling routines not found in the EXEC library.
*/
void *
NextNode(node)
NODE *node;
{
node = node->mln_Succ;
if (node->mln_Succ == NULL)
return(NULL);
return(node);
}
void *
GetHead(list)
LIST *list;
{
if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
return(list->mlh_Head);
return(NULL);
}
/*
* Compare two names which are at least n characters long each,
* ignoring case.
*/
nccmp(p1,p2,n)
register ubyte *p1, *p2;
register short n;
{
while (--n >= 0) {
if ((p1[n]|0x20) != (p2[n]|0x20))
return(0);
}
return(1);
}
/*
* Create a file or directory and link it into it's parent directory.
*/
RAMFILE *
createramfile(parentdir, type, name)
RAMFILE *parentdir;
char *name;
{
register RAMFILE *ramfile;
ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
AddTail(&parentdir->list, ramfile);
ramfile->parent = parentdir;
ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
strcpy(ramfile->name, name);
ramfile->type = type;
ramfile->protection = 0;
NewList(&ramfile->list);
DateStamp(&ramfile->date);
DateStamp(&ramfile->parent->date);
return(ramfile);
}
/*
* Free all data associated with a file
*/
void
freedata(ramfile)
RAMFILE *ramfile;
{
FENTRY *fen;
TotalBytes -= ramfile->bytes;
while (fen = RemHead(&ramfile->list)) {
dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes);
FreeMem(fen->buf, fen->bytes);
FreeMem(fen, sizeof(*fen));
}
ramfile->bytes = 0;
DateStamp(&ramfile->date);
DateStamp(&ramfile->parent->date);
}
/*
* Unlink and remove a file. Any data associated with the file or
* directory has already been freed up.
*/
void
freeramfile(ramfile)
RAMFILE *ramfile;
{
Remove(ramfile); /* unlink from parent directory */
if (ramfile->name)
FreeMem(ramfile->name,strlen(ramfile->name)+1);
if (ramfile->comment)
FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
FreeMem(ramfile,sizeof(*ramfile));
}
/*
* The lock function. The file has already been checked to see if it
* is lockable given the mode.
*/
LOCK *
ramlock(ramfile, mode)
RAMFILE *ramfile;
{
LOCK *lock = dosalloc(sizeof(LOCK));
LOCKLINK *ln;
if (mode != ACCESS_WRITE)
mode = ACCESS_READ;
ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC);
AddHead(&LCBase,ln);
ln->lock = lock;
lock->fl_Link= (long)ln;
lock->fl_Key = (long)ramfile;
lock->fl_Access = mode;
lock->fl_Task = &DosProc->pr_MsgPort;
lock->fl_Volume = (BPTR)CTOB(DosNode);
if (mode == ACCESS_READ)
++ramfile->locks;
else
ramfile->locks = -1;
return(lock);
}
void
ramunlock(lock)
LOCK *lock;
{
RAMFILE *file = (RAMFILE *)lock->fl_Key;
Remove(lock->fl_Link); /* unlink from list */
FreeMem(lock->fl_Link, sizeof(LOCKLINK)); /* free link node */
if (lock->fl_Access == ACCESS_READ) /* undo lock effect */
--file->locks;
else
file->locks = 0;
dosfree(lock); /* free lock */
}
/*
* GETLOCKFILE(bptrlock)
*
* Return the RAMFILE entry (file or directory) associated with the
* given lock, which is passed as a BPTR.
*
* According to the DOS spec, the only way a NULL lock will ever be
* passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
* In anycase, If a NULL lock is passed to me I simply assume it means
* the root directory of the RAM disk.
*/
RAMFILE *
getlockfile(lock)
void *lock; /* actually BPTR to LOCK */
{
register LOCK *rl = BTOC(lock);
if (rl)
return((RAMFILE *)rl->fl_Key);
return(&RFRoot);
}
/*
* Search the specified path beginning at the specified directory.
* The directory pointer is updated to the directory containing the
* actual file. Return the file node or NULL if not found. If the
* path is illegal (an intermediate directory was not found), set *ppar
* to NULL and return NULL.
*
* *ppar may also be set to NULL if the search path IS the root.
*
* If pptr not NULL, Set *pptr to the final component in the path.
*/
RAMFILE *
searchpath(ppar,buf,pptr)
RAMFILE **ppar;
char *buf;
char **pptr;
{
RAMFILE *file = *ppar;
RAMFILE *srch;
short len;
char *ptr;
*ppar = NULL;
for (;*buf && file;) {
ptr = getpathelement(&buf,&len);
if (buf[0] == ':') { /* go to root */
++buf;
file = &RFRoot;
continue;
}
if (*ptr == '/') { /* go back a directory */
if (!file->parent) { /* no parent directory */
return(NULL);
}
file = file->parent;
continue;
}
if (file->type == FILE_FILE)
return(NULL);
for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
file = srch; /* element found */
break;
}
}
if (srch == NULL) {
if (*buf == 0) /* Element not found. If it was the final */
*ppar = file; /* element the parent directory is valid */
if (pptr)
*pptr = ptr;
return(NULL);
}
}
if (pptr)
*pptr = ptr;
*ppar = file->parent;
return(file);
}
/*
* Return the next path element in the string. The routine effectively
* removes any trailing '/'s, but treats ':' as part of the next component
* (i.e. ':' is checked and skipped in SEARCHPATH()).
*/
char *
getpathelement(pstr,plen)
char **pstr;
short *plen;
{
char *base;
register char *ptr = *pstr;
register short len = 0;
if (*(base = ptr)) {
if (*ptr == '/') {
++ptr;
++len;
} else {
while (*ptr && *ptr != '/' && *ptr != ':') {
++ptr;
++len;
}
if (*ptr == '/')
++ptr;
}
}
*pstr = ptr;
*plen = len;
return(base);
}
char *
typetostr(ty)
{
switch(ty) {
case ACTION_DIE: return("DIE");
case ACTION_OPENRW: return("OPEN-RW");
case ACTION_OPENOLD: return("OPEN-OLD");
case ACTION_OPENNEW: return("OPEN-NEW");
case ACTION_READ: return("READ");
case ACTION_WRITE: return("WRITE");
case ACTION_CLOSE: return("CLOSE");
case ACTION_SEEK: return("SEEK");
case ACTION_EXAMINE_NEXT: return("EXAMINE NEXT");
case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ");
case ACTION_INFO: return("INFO");
case ACTION_DISK_INFO: return("DISK INFO");
case ACTION_PARENT: return("PARENTDIR");
case ACTION_DELETE_OBJECT: return("DELETE");
case ACTION_CREATE_DIR: return("CREATEDIR");
case ACTION_LOCATE_OBJECT: return("LOCK");
case ACTION_COPY_DIR: return("DUPLOCK");
case ACTION_FREE_LOCK: return("FREELOCK");
case ACTION_SET_PROTECT: return("SETPROTECT");
case ACTION_SET_COMMENT: return("SETCOMMENT");
case ACTION_RENAME_OBJECT: return("RENAME");
case ACTION_INHIBIT: return("INHIBIT");
case ACTION_RENAME_DISK: return("RENAME DISK");
case ACTION_MORECACHE: return("MORE CACHE");
case ACTION_WAIT_CHAR: return("WAIT FOR CHAR");
case ACTION_FLUSH: return("FLUSH");
case ACTION_RAWMODE: return("RAWMODE");
default: return("---------UNKNOWN-------");
}
}
/*
* DEBUGGING CODE. You cannot make DOS library calls that access other
* devices from within a DOS device driver because they use the same
* message port as the driver. If you need to make such calls you must
* create a port and construct the DOS messages yourself. I do not
* do this. To get debugging info out another PROCESS is created to which
* debugging messages can be sent.
*
* You want the priority of the debug process to be larger than the
* priority of your DOS handler. This is so if your DOS handler crashes
* you have a better idea of where it died from the debugging messages
* (remember that the two processes are asyncronous from each other).
*/
extern void debugproc();
dbinit()
{
TASK *task = FindTask(NULL);
Dback = CreatePort(NULL,NULL);
CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096);
WaitPort(Dback); /* handshake startup */
GetMsg(Dback); /* remove dummy msg */
dbprintf("Debugger running V1.10, 2 November 1987\n");
dbprintf("Works with WORKBENCH!\n");
}
dbuninit()
{
MSG killmsg;
if (Dbport) {
killmsg.mn_Length = 0; /* 0 means die */
PutMsg(Dbport,&killmsg);
WaitPort(Dback); /* He's dead jim! */
GetMsg(Dback);
DeletePort(Dback);
/*
* Since the debug process is running at a greater priority, I
* am pretty sure that it is guarenteed to be completely removed
* before this task gets control again. Still, it doesn't hurt...
*/
Delay(50); /* ensure he's dead */
}
}
dbprintf(a,b,c,d,e,f,g,h,i,j)
{
char buf[256];
MSG *msg;
if (Dbport && !DBDisable) {
sprintf(buf,a,b,c,d,e,f,g,h,i,j);
msg = AllocMem(sizeof(MSG)+strlen(buf)+1, MEMF_PUBLIC|MEMF_CLEAR);
msg->mn_Length = strlen(buf)+1; /* Length NEVER 0 */
strcpy(msg+1,buf);
PutMsg(Dbport,msg);
}
}
/*
* BTW, the DOS library used by debugmain() was actually openned by
* the device driver. Note: DummyMsg cannot be on debugmain()'s stack
* since debugmain() goes away on the final handshake.
*/
debugmain()
{
MSG *msg;
short len;
void *fh;
Dbport = CreatePort(NULL,NULL);
fh = Open("con:0/0/640/100/debugwindow", 1006);
PutMsg(Dback, &DummyMsg);
for (;;) {
WaitPort(Dbport);
msg = GetMsg(Dbport);
len = msg->mn_Length;
if (len == 0)
break;
--len; /* Fix length up */
Write(fh, msg+1, len);
FreeMem(msg,sizeof(MSG)+len+1);
}
Close(fh);
DeletePort(Dbport);
PutMsg(Dback,&DummyMsg); /* Kill handshake */
}
/*
* The assembly tag for the DOS process: CNOP causes alignment problems
* with the Aztec assembler for some reason. I assume then, that the
* alignment is unknown. Since the BCPL conversion basically zero's the
* lower two bits of the address the actual code may start anywhere around
* the label.... Sigh.... (see CreatProc() above).
*/
#asm
public _debugproc
public _debugmain
cseg
nop
nop
nop
_debugproc:
nop
nop
movem.l D2-D7/A2-A6,-(sp)
jsr _debugmain
movem.l (sp)+,D2-D7/A2-A6
rts
#endasm